// Package validator 验证器，主要规则验证
package validator

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/duke-git/lancet/v2/validator"
	"net"
	"net/mail"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"
)

var (
	alphaMatcher         *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
	letterRegexMatcher   *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
	numberRegexMatcher   *regexp.Regexp = regexp.MustCompile(`\d`)
	intStrMatcher        *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
	urlMatcher           *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
	dnsMatcher           *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
	emailMatcher         *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
	chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1[3456789]\d{9}$`)
	chineseIdMatcher     *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`)
	chineseMatcher       *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
	chinesePhoneMatcher  *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`)
	creditCardMatcher    *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
	base64Matcher        *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
	base64URLMatcher     *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`)
	binMatcher           *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`)
	hexMatcher           *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`)
	visaMatcher          *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`)
	// master 信用卡号匹配器
	masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`)
	// 美国快递单号匹配器
	americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`)
	// 银联账号匹配器
	unionPay *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$")
	// 中国银联支付账号匹配器
	chinaUnionPay *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`)
)

var (
	// 身份证号码公式
	factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	// 效验位值
	verifyCode = [11]string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}
	// 身份代码
	chineseIDCardProvince = map[string]struct{}{
		"11": {},
		"12": {},
		"13": {},
		"14": {},
		"15": {},
		"21": {},
		"22": {},
		"23": {},
		"31": {},
		"32": {},
		"33": {},
		"34": {},
		"35": {},
		"36": {},
		"37": {},
		"41": {},
		"42": {},
		"43": {},
		"44": {},
		"45": {},
		"46": {},
		"50": {},
		"51": {},
		"52": {},
		"53": {},
		"54": {},
		"61": {},
		"62": {},
		"63": {},
		"64": {},
		"65": {},
		"71": {},
		"81": {},
		"82": {},
		"91": {},
		"99": {},
	}
	// 身份证开始年份
	birthStartYear = 1900
)

// IsAlpha 验证 是否包含字母
// @param string str 验证值
// @return bool
func IsAlpha(str string) bool {
	return alphaMatcher.MatchString(str)
}

// IsUpper 检查是否大写字母
// @param string str 验证值
// @return bool
func IsUpper(str string) bool {
	for _, r := range str {
		if !unicode.IsUpper(r) {
			return false
		}
	}
	return str != ""
}

// IsLower 检查是否全是小写字母
// string str 验证值
// 返回 bool
func IsLower(str string) bool {
	for _, r := range str {
		if !unicode.IsLower(r) {
			return false
		}
	}
	return str != ""
}

// IsASCII 检查是否Ascii字符
// string str 验证值
// 返回 bool
func IsASCII(str string) bool {
	for _, r := range str {
		if r > unicode.MaxASCII {
			return false
		}
	}
	return true
}

// IsPrintable 检查是否可打印字符
// string str 验证值
// 返回 bool
func IsPrintable(str string) bool {
	for _, r := range str {
		if !unicode.IsPrint(r) {
			if r == '\n' || r == '\r' || r == '\t' || r == '`' {
				continue
			}
			return false
		}
	}
	return true
}

// IsContainUpper 检查是否包含大写字母
// string str 验证值
// 返回 bool
func IsContainUpper(str string) bool {
	for _, r := range str {
		if unicode.IsUpper(r) && unicode.IsLetter(r) {
			return true
		}
	}
	return false
}

// IsContainLower 检查是否包含小写字母
// string str 验证值
// 返回 bool
func IsContainLower(str string) bool {
	for _, r := range str {
		if unicode.IsLower(r) && unicode.IsLetter(r) {
			return true
		}
	}
	return false
}

// IsContainLetter 检查是否包含字母
// string str 验证值
// 返回 bool
func IsContainLetter(str string) bool {
	return letterRegexMatcher.MatchString(str)
}

// IsContainNumber 检查是否包含数字
// string str
// 返回 bool
func IsContainNumber(input string) bool {
	return numberRegexMatcher.MatchString(input)
}

// IsJson 检查是否是json格式
// string input
// 返回 bool
func IsJson(input string) bool {
	var js json.RawMessage
	return json.Unmarshal([]byte(input), &js) == nil
}

// IsFloatStr 检查是否是浮点数
// string str
// 返回 bool
func IsFloatStr(str string) bool {
	_, e := strconv.ParseFloat(str, 64)
	return e == nil
}

// IsIntStr 检查是否是整数
// string str
// 返回 bool
func IsIntStr(str string) bool {
	return intStrMatcher.MatchString(str)
}

//	IsNumberStr 检查是否是数字
//
// string s
// 返回 bool
func IsNumberStr(s string) bool {
	return IsIntStr(s) || IsFloatStr(s)
}

// IsIp 检查是否合法IP地址
// ipStr string
// 返回 bool
func IsIp(ipStr string) bool {
	ip := net.ParseIP(ipStr)
	return ip != nil
}

// IsIpV4 检查是否合法IPv4地址
// ipStr string
// 返回 bool
func IsIpV4(ipStr string) bool {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return false
	}
	return ip.To4() != nil
}

// IsIpV6 检查是否合法IPv6地址
// ipStr string
// 返回 bool
func IsIpV6(ipStr string) bool {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return false
	}
	return ip.To4() == nil && len(ip) == net.IPv6len
}

// IsPort 检查是否是端口号
// string str
// 返回 bool
func IsPort(str string) bool {
	if i, err := strconv.ParseInt(str, 10, 64); err == nil && i > 0 && i < 65536 {
		return true
	}
	return false
}

// IsUrl 检查是否是url
// string str
// 返回 bool
func IsUrl(str string) bool {
	if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
		return false
	}
	u, err := url.Parse(str)
	if err != nil {
		return false
	}
	if strings.HasPrefix(u.Host, ".") {
		return false
	}
	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
		return false
	}

	return urlMatcher.MatchString(str)
}

// IsDns 检查是否是域名
// string dns
// 返回 bool
func IsDns(dns string) bool {
	return dnsMatcher.MatchString(dns)
}

// IsEmail 检查是否是邮箱
// string email
// 返回 bool
func IsEmail(email string) bool {
	_, err := mail.ParseAddress(email)
	return err == nil
}

// IsChineseMobile 检查是否是手机号
// string mobileNum
// 返回 bool
func IsChineseMobile(mobileNum string) bool {
	return chineseMobileMatcher.MatchString(mobileNum)
}

// IsChineseIdNum 验证身份证
// string id
// 返回 bool
func IsChineseIdNum(id string) bool {
	// 所有号码都为数字或x组成
	if !chineseIdMatcher.MatchString(id) {
		return false
	}

	// 参考 GB/T2260,验证身份代码
	_, ok := chineseIDCardProvince[id[0:2]]
	if !ok {
		return false
	}

	// 验证出生年月
	birthStr := fmt.Sprintf("%s-%s-%s", id[6:10], id[10:12], id[12:14])
	birthday, err := time.Parse("2006-01-02", birthStr)
	if err != nil || birthday.After(time.Now()) || birthday.Year() < birthStartYear {
		return false
	}

	// 验证码
	sum := 0
	for i, c := range id[:17] {
		v, _ := strconv.Atoi(string(c))
		sum += v * factor[i]
	}

	return verifyCode[sum%11] == strings.ToUpper(id[17:18])
}

// IsContainChinese 检查是否包含中文字符
// string s
// 返回 bool
func IsContainChinese(s string) bool {
	return chineseMatcher.MatchString(s)
}

// IsChinesePhone 检查是否是手机号
// string phone
// 返回 bool
func IsChinesePhone(phone string) bool {
	return chinesePhoneMatcher.MatchString(phone)
}

// IsCreditCard 检查是否是银行卡号
// string creditCart
// 返回 bool
func IsCreditCard(creditCart string) bool {
	return creditCardMatcher.MatchString(creditCart)
}

// IsBase64 检查是否是base64编码
// string base64
// 返回 bool
func IsBase64(base64 string) bool {
	return base64Matcher.MatchString(base64)
}

// IsEmptyString 判断是否是空字符串
// string str
// 返回 bool
func IsEmptyString(str string) bool {
	return len(str) == 0
}

// IsRegexMatch 判断是否匹配正则表达式
// string str, string regex
// 返回 bool
func IsRegexMatch(str, regex string) bool {
	reg := regexp.MustCompile(regex)
	return reg.MatchString(str)
}

// IsStrongPassword 判断是否是强密码
// string password, int length
// 返回 bool
func IsStrongPassword(password string, length int) bool {
	if len(password) < length {
		return false
	}
	var num, lower, upper, special bool
	for _, r := range password {
		switch {
		case unicode.IsDigit(r):
			num = true
		case unicode.IsUpper(r):
			upper = true
		case unicode.IsLower(r):
			lower = true
		case unicode.IsSymbol(r), unicode.IsPunct(r):
			special = true
		}
	}

	return num && lower && upper && special
}

// IsWeakPassword 判断是否是弱密码
// string password
// 返回 bool
func IsWeakPassword(password string) bool {
	var num, letter, special bool
	for _, r := range password {
		switch {
		case unicode.IsDigit(r):
			num = true
		case unicode.IsLetter(r):
			letter = true
		case unicode.IsSymbol(r), unicode.IsPunct(r):
			special = true
		}
	}

	return (num || letter) && !special
}

// IsZeroValue 判断是否是零值
// any value
// 返回 bool
func IsZeroValue(value any) bool {
	if value == nil {
		return true
	}

	rv := reflect.ValueOf(value)
	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}

	if !rv.IsValid() {
		return true
	}

	switch rv.Kind() {
	case reflect.String:
		return rv.Len() == 0
	case reflect.Bool:
		return !rv.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return rv.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return rv.Float() == 0
	case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map:
		return rv.IsNil()
	}

	return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface())
}

// IsGBK 判断是否是GBK编码
// []byte data
// 返回 bool
func IsGBK(data []byte) bool {
	i := 0
	for i < len(data) {
		if data[i] <= 0xff {
			i++
			continue
		}
		if data[i] >= 0x81 &&
			data[i] <= 0xfe &&
			data[i+1] >= 0x40 &&
			data[i+1] <= 0xfe &&
			data[i+1] != 0xf7 {
			i += 2
			continue
		}
		return false
	}
	return true
}

// IsNumber 判断是否是数字
// any v
// 返回 bool
func IsNumber(v any) bool {
	return IsInt(v) || IsFloat(v)
}

// IsFloat 判断是否是浮点数
// any v
// 返回 bool
func IsFloat(v any) bool {
	switch v.(type) {
	case float32, float64:
		return true
	}
	return false
}

// IsInt 判断是否是整数
// any v
// 返回 bool
func IsInt(v any) bool {
	switch v.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
		return true
	}
	return false
}

// IsBin 判断是否是二进制
// string v
// 返回 bool
func IsBin(v string) bool {
	return binMatcher.MatchString(v)
}

// IsHex 判断是否是十六进制
// string v
// 返回 bool
func IsHex(v string) bool {
	return hexMatcher.MatchString(v)
}

// IsVisa 判断是否是Visa卡号
// string v
// 返回 bool
func IsVisa(v string) bool {
	return visaMatcher.MatchString(v)
}

// IsMasterCard 判断是否是MasterCard卡号
// string v
// 返回 bool
func IsMasterCard(v string) bool {
	return masterCardMatcher.MatchString(v)
}

// IsAmericanExpress 判断是否是AmericanExpress卡号
// string v
// 返回 bool
func IsAmericanExpress(v string) bool {
	return americanExpressMatcher.MatchString(v)
}

// IsUnionPay 判断是否是UnionPay卡号
// string v
// 返回 bool
func IsUnionPay(v string) bool {
	return unionPay.MatchString(v)
}

// IsChinaUnionPay 判断是否是ChinaUnionPay卡号
// string v
// 返回 bool
func IsChinaUnionPay(v string) bool {
	return chinaUnionPay.MatchString(v)
}

// IsBase64URL 判断是否是Base64URL
// string v
// 返回 bool
func IsBase64URL(v string) bool {
	return base64URLMatcher.MatchString(v)
}

// AddOperationPermission 增加操作权限
// uint currentPermisson 当前权限
// uint newPermission 新增权限
// 返回 uint
func AddOperationPermission(currentPermisson uint, newPermission uint) uint {
	return currentPermisson | newPermission
}

// IsOperationPermission 判断是否拥有操作权限
// uint currentPermisson 当前权限
// uint newPermission 新增权限
// 返回 bool
func IsOperationPermission(currentPermisson uint, newPermission uint) bool {
	return currentPermisson&newPermission == newPermission
}

// DelOperationPermission 删除操作权限
// uint currentPermisson 当前权限
// uint newPermission 新增权限
func DelOperationPermission(currentPermisson uint, newPermission uint) uint {
	return currentPermisson &^ newPermission
}

// IsValidUniqueCreditCode 校验统一社会信用代码
// string code 信用代码
// 返回 bool
func IsValidUniqueCreditCode(code string) bool {
	// 统一社会信用代码正则表达式
	pattern := `^[0-9ABCDEFGHJKLMNPQRTUWXY]{2}\d{6}[0-9ABCDEFGHJKLMNPQRTUWXY]{10}$`
	match, err := regexp.MatchString(pattern, code)
	if err != nil || !match {
		return false
	}

	// 校验码校验
	weights := []int{1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28}
	codes := "0123456789ABCDEFGHJKLMNPQRTUWXY"
	sum := 0
	for i, c := range code[:17] {
		w := weights[i]
		idx := strings.IndexRune(codes, c)
		sum += idx * w
	}
	check := 31 - sum%31
	if check == 31 {
		check = 0
	}
	return string(codes[check]) == code[17:]
}

// IsRealJson 判断字符串是否是合法的json
// string s
// 返回 bool
func IsRealJson(s string) bool {
	if validator.IsEmptyString(s) {
		return false
	}
	return json.Valid([]byte(s))
}

// IsNil 针对非值类型判断是否是nil
// any v
// 返回 bool
func IsNil(v any) bool {
	if v == nil {
		return true
	}
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
		return rv.IsNil()
	default:
		return false
	}
}

// IsBaseType 判断是否基础类型
// reflect.Kind k
// 返回 bool
func IsBaseType(k reflect.Kind) bool {
	if reflect.String == k {
		return true
	}
	return k > reflect.Invalid && k <= reflect.Float64
}

// IsEqual 判断是否相等
// any src
// any dst
// 返回 bool
func IsEqual(src, dst any) bool {
	if src == nil || dst == nil {
		return src == dst
	}
	bsSrc, ok := src.([]byte)
	if !ok {
		return reflect.DeepEqual(src, dst)
	}
	bsDst, ok := dst.([]byte)
	if !ok {
		return false
	}
	if bsSrc == nil || bsDst == nil {
		return bsSrc == nil && bsDst == nil
	}
	return bytes.Equal(bsSrc, bsDst)
}
